home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Players / PlayWndASF / playwndasf.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  34.3 KB  |  1,262 lines

  1. //------------------------------------------------------------------------------
  2. // File: PlayWndASF.cpp
  3. //
  4. // Desc: DirectShow sample code - a simple audio/video media file player
  5. //       application for Windows Media content (ASF, WMV, WMA).  
  6. //       Pause, stop, mute, and fullscreen mode toggle can be performed 
  7. //       via keyboard commands.
  8. //
  9. // Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.
  10. //------------------------------------------------------------------------------
  11.  
  12.  
  13. #include <dshow.h>
  14. #include <commctrl.h>
  15. #include <commdlg.h>
  16. #include <stdio.h>
  17. #include <tchar.h>
  18. #include <atlbase.h>
  19.  
  20. #include "playwndasf.h"
  21. #include "resource.h"
  22. #include "keyprovider.h"
  23.  
  24. // An application can advertise the existence of its filter graph
  25. // by registering the graph with a global Running Object Table (ROT).
  26. // The GraphEdit application can detect and remotely view the running
  27. // filter graph, allowing you to 'spy' on the graph with GraphEdit.
  28. //
  29. // To enable registration in this sample, define REGISTER_FILTERGRAPH.
  30. //
  31. #define REGISTER_FILTERGRAPH
  32.  
  33. //
  34. // Global data
  35. //
  36. HWND      ghApp=0;
  37. HMENU     ghMenu=0;
  38. HINSTANCE ghInst=0;
  39. TCHAR     g_szFileName[MAX_PATH]={0};
  40. BOOL      g_bAudioOnly=FALSE, g_bFullscreen=FALSE;
  41. LONG      g_lVolume=VOLUME_FULL;
  42. DWORD     g_dwGraphRegister=0;
  43. PLAYSTATE g_psCurrent=Stopped;
  44. BOOL      g_bUseNewASFReader=TRUE;
  45.  
  46. // DirectShow interfaces
  47. IGraphBuilder *pGB = NULL;
  48. IMediaControl *pMC = NULL;
  49. IMediaEventEx *pME = NULL;
  50. IVideoWindow  *pVW = NULL;
  51. IBasicAudio   *pBA = NULL;
  52. IBasicVideo   *pBV = NULL;
  53. IMediaSeeking *pMS = NULL;
  54. IVideoFrameStep *pFS = NULL;
  55.  
  56. // Global key provider object created/released during the
  57. // Windows Media graph-building stage.
  58. CKeyProvider prov;
  59.  
  60.  
  61.  
  62. HRESULT PlayMovieInWindow(LPTSTR szFile)
  63. {
  64.     USES_CONVERSION;
  65.     WCHAR wFile[MAX_PATH];
  66.     HRESULT hr;
  67.     IFileSourceFilter *pFSF=NULL;
  68.     IBaseFilter *pReader=NULL;
  69.  
  70.     // Clear open dialog remnants before calling RenderFile()
  71.     UpdateWindow(ghApp);
  72.  
  73.     // Convert filename to wide character string
  74.     wcscpy(wFile, T2W(szFile));
  75.  
  76.     // Get the interface for DirectShow's GraphBuilder
  77.     JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
  78.                          IID_IGraphBuilder, (void **)&pGB));
  79.  
  80.     if(SUCCEEDED(hr))
  81.     {
  82.         // Use special handling for Windows Media files
  83.         if (IsWindowsMediaFile(szFile))
  84.         {
  85.             if (g_bUseNewASFReader)
  86.             {
  87.                 // Load the improved ASF reader filter by CLSID
  88.                 hr = CreateFilter(CLSID_WMAsfReader, &pReader);
  89.                 if(FAILED(hr))
  90.                 {
  91.                     Msg(TEXT("Failed to create WMAsfWriter filter!  hr=0x%x\n"), hr);
  92.                     return hr;
  93.                 }
  94.  
  95.                 // Add the ASF reader filter to the graph.  For ASF/WMV/WMA content,
  96.                 // this filter is NOT the default and must be added explicitly.
  97.                 hr = pGB->AddFilter(pReader, L"ASF Reader");
  98.                 if(FAILED(hr))
  99.                 {
  100.                     Msg(TEXT("Failed to add ASF reader filter to graph!  hr=0x%x\n"), hr);
  101.                     return hr;
  102.                 }
  103.  
  104.                 // Create the key provider that will be used to unlock the WM SDK
  105.                 JIF(AddKeyProvider(pGB));
  106.  
  107.                 // Set its source filename
  108.                 JIF(pReader->QueryInterface(IID_IFileSourceFilter, (void **) &pFSF));
  109.                 JIF(pFSF->Load(wFile, NULL));
  110.                 pFSF->Release();
  111.  
  112.                 // Render the output pins of the ASF reader to build the
  113.                 // remainder of the graph automatically
  114.                 JIF(RenderOutputPins(pGB, pReader));
  115.  
  116.                 // Since the graph is built and the filters are added to the graph,
  117.                 // the WM ASF reader interface can be released.
  118.                 pReader->Release();
  119.             }
  120.  
  121.             // Use the legacy ASF reader filter (default reader) with RenderFile
  122.             else    
  123.             {
  124.                 // Create the key provider that will be used to unlock the WM SDK
  125.                 JIF(AddKeyProvider(pGB));
  126.  
  127.                 // Have the graph builder construct the remainder of the graph
  128.                 JIF(pGB->RenderFile(wFile, NULL));
  129.             }
  130.         }
  131.  
  132.         // Not a Windows Media file, so just render the standard way
  133.         else
  134.         {
  135.             // Have the graph builder construct its the appropriate graph automatically
  136.             JIF(pGB->RenderFile(wFile, NULL));
  137.         }
  138.  
  139.         // QueryInterface for DirectShow interfaces
  140.         JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
  141.         JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
  142.         JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));
  143.  
  144.         // Query for video interfaces, which may not be relevant for audio files
  145.         JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
  146.         JIF(pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV));
  147.  
  148.         // Query for audio interfaces, which may not be relevant for video-only files
  149.         JIF(pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA));
  150.  
  151.         // Is this an audio-only file (no video component)?
  152.         CheckVisibility();
  153.  
  154.         // Have the graph signal event via window callbacks for performance
  155.         JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));
  156.  
  157.         if (!g_bAudioOnly)
  158.         {
  159.             JIF(pVW->put_Owner((OAHWND)ghApp));
  160.             JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));
  161.  
  162.             JIF(InitVideoWindow(1, 1));
  163.             GetFrameStepInterface();
  164.         }
  165.         else
  166.         {
  167.             JIF(InitPlayerWindow());
  168.         }
  169.  
  170.         // Let's get ready to rumble!
  171.         CheckSizeMenu(ID_FILE_SIZE_NORMAL);
  172.         ShowWindow(ghApp, SW_SHOWNORMAL);
  173.         UpdateWindow(ghApp);
  174.         SetForegroundWindow(ghApp);
  175.         SetFocus(ghApp);
  176.         g_bFullscreen = FALSE;
  177.         UpdateMainTitle();
  178.  
  179. #ifdef REGISTER_FILTERGRAPH
  180.         hr = AddGraphToRot(pGB, &g_dwGraphRegister);
  181.         if (FAILED(hr))
  182.         {
  183.             Msg(TEXT("Failed to register filter graph with ROT!  hr=0x%x"), hr);
  184.             g_dwGraphRegister = 0;
  185.         }
  186. #endif
  187.  
  188.         // Run the graph to play the media file
  189.         JIF(pMC->Run());
  190.         g_psCurrent=Running;
  191.  
  192.         SetFocus(ghApp);
  193.     }
  194.  
  195.     return hr;
  196. }
  197.  
  198.  
  199. HRESULT RenderOutputPins(IGraphBuilder *pGB, IBaseFilter *pFilter)
  200. {
  201.     HRESULT            hr = S_OK;
  202.     IEnumPins *        pEnumPin = NULL;
  203.     IPin *            pConnectedPin = NULL, * pPin = NULL;
  204.     PIN_DIRECTION    PinDirection;
  205.     ULONG            ulFetched;
  206.  
  207.     // Enumerate all pins on the filter
  208.     hr = pFilter->EnumPins(&pEnumPin);
  209.  
  210.     if(SUCCEEDED(hr))
  211.     {
  212.         // Step through every pin, looking for the output pins
  213.         while (S_OK == (hr = pEnumPin->Next(1L, &pPin, &ulFetched)))
  214.         {
  215.             // Is this pin connected?  We're not interested in connected pins.
  216.             hr = pPin->ConnectedTo(&pConnectedPin);
  217.             if (pConnectedPin)
  218.             {
  219.                 pConnectedPin->Release();
  220.                 pConnectedPin = NULL;
  221.             }
  222.  
  223.             // If this pin is not connected, render it.
  224.             if (VFW_E_NOT_CONNECTED == hr)
  225.             {
  226.                 hr = pPin->QueryDirection(&PinDirection);
  227.                 if ((S_OK == hr) && (PinDirection == PINDIR_OUTPUT))
  228.                 {
  229.                     hr = pGB->Render(pPin);
  230.                 }
  231.             }
  232.             pPin->Release();
  233.  
  234.             // If there was an error, stop enumerating
  235.             if (FAILED(hr))                      
  236.                 break;
  237.         }
  238.     }
  239.  
  240.     // Release pin enumerator
  241.     pEnumPin->Release();
  242.     return hr;
  243. }
  244.  
  245.  
  246. HRESULT AddKeyProvider(IGraphBuilder *pGraph)
  247. {
  248.     HRESULT hr;
  249.  
  250.     // Instantiate the key provider class, and AddRef it
  251.     // so that COM doesn't try to free our static object.
  252.     prov.AddRef();
  253.  
  254.     // Give the graph an IObjectWithSite pointer to us for callbacks & QueryService.
  255.     IObjectWithSite* pObjectWithSite = NULL;
  256.  
  257.     hr = pGraph->QueryInterface(IID_IObjectWithSite, (void**)&pObjectWithSite);
  258.     if (SUCCEEDED(hr))
  259.     {
  260.         // Use the IObjectWithSite pointer to specify our key provider object.
  261.         // The filter graph manager will use this pointer to call
  262.         // QueryService to do the unlocking.
  263.         // If the unlocking succeeds, then we can build our graph.
  264.             
  265.         hr = pObjectWithSite->SetSite((IUnknown *) (IServiceProvider *) &prov);
  266.         pObjectWithSite->Release();
  267.     }
  268.  
  269.     return hr;
  270. }
  271.  
  272.  
  273. HRESULT CreateFilter(REFCLSID clsid, IBaseFilter **ppFilter)
  274. {
  275.     HRESULT hr;
  276.  
  277.     hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
  278.         IID_IBaseFilter,
  279.         (void **) ppFilter);
  280.  
  281.     if(FAILED(hr))
  282.     {
  283.         Msg(TEXT("CreateFilter: Failed to create filter!  hr=0x%x\n"), hr);
  284.         if (ppFilter)
  285.             *ppFilter = NULL;
  286.         return hr;
  287.     }
  288.  
  289.     return S_OK;
  290. }
  291.  
  292.  
  293. BOOL IsWindowsMediaFile(LPTSTR lpszFile)
  294. {
  295.     if (_tcsstr(lpszFile, TEXT(".asf")) ||
  296.         _tcsstr(lpszFile, TEXT(".ASF")) ||
  297.         _tcsstr(lpszFile, TEXT(".wma")) ||
  298.         _tcsstr(lpszFile, TEXT(".WMA")) ||
  299.         _tcsstr(lpszFile, TEXT(".wmv")) ||
  300.         _tcsstr(lpszFile, TEXT(".WMV")))
  301.         return TRUE;
  302.     else
  303.         return FALSE;
  304. }
  305.  
  306. HRESULT InitVideoWindow(int nMultiplier, int nDivider)
  307. {
  308.     LONG lHeight, lWidth;
  309.     HRESULT hr = S_OK;
  310.     RECT rect;
  311.  
  312.     if (!pBV)
  313.         return S_OK;
  314.  
  315.     // Read the default video size
  316.     hr = pBV->GetVideoSize(&lWidth, &lHeight);
  317.     if (hr == E_NOINTERFACE)
  318.         return S_OK;
  319.  
  320.     EnablePlaybackMenu(TRUE);
  321.  
  322.     // Account for requests of normal, half, or double size
  323.     lWidth  = lWidth  * nMultiplier / nDivider;
  324.     lHeight = lHeight * nMultiplier / nDivider;
  325.  
  326.     SetWindowPos(ghApp, NULL, 0, 0, lWidth, lHeight, 
  327.                  SWP_NOMOVE | SWP_NOOWNERZORDER);
  328.  
  329.     int nTitleHeight  = GetSystemMetrics(SM_CYCAPTION);
  330.     int nBorderWidth  = GetSystemMetrics(SM_CXBORDER);
  331.     int nBorderHeight = GetSystemMetrics(SM_CYBORDER);
  332.  
  333.     // Account for size of title bar and borders for exact match
  334.     // of window client area to default video size
  335.     SetWindowPos(ghApp, NULL, 0, 0, lWidth + 2*nBorderWidth,
  336.             lHeight + nTitleHeight + 2*nBorderHeight,
  337.             SWP_NOMOVE | SWP_NOOWNERZORDER);
  338.  
  339.     GetClientRect(ghApp, &rect);
  340.     JIF(pVW->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom));
  341.  
  342.     return hr;
  343. }
  344.  
  345.  
  346. HRESULT InitPlayerWindow(void)
  347. {
  348.     // Reset to a default size for audio and after closing a clip
  349.     SetWindowPos(ghApp, NULL, 0, 0,
  350.                  DEFAULT_AUDIO_WIDTH,
  351.                  DEFAULT_AUDIO_HEIGHT,
  352.                  SWP_NOMOVE | SWP_NOOWNERZORDER);
  353.  
  354.     // Check the 'full size' menu item
  355.     CheckSizeMenu(ID_FILE_SIZE_NORMAL);
  356.     EnablePlaybackMenu(FALSE);
  357.  
  358.     return S_OK;
  359. }
  360.  
  361.  
  362. void MoveVideoWindow(void)
  363. {
  364.     HRESULT hr;
  365.     
  366.     // Track the movement of the container window and resize as needed
  367.     if(pVW)
  368.     {
  369.         RECT client;
  370.  
  371.         GetClientRect(ghApp, &client);
  372.         hr = pVW->SetWindowPosition(client.left, client.top, 
  373.                                     client.right, client.bottom);
  374.     }
  375. }
  376.  
  377.  
  378. void CheckVisibility(void)
  379. {
  380.     long lVisible;
  381.     HRESULT hr;
  382.  
  383.     if ((!pVW) || (!pBV))
  384.     {
  385.         // Audio-only files have no video interfaces.  This might also
  386.         // be a file whose video component uses an unknown video codec.
  387.         g_bAudioOnly = TRUE;
  388.         return;
  389.     }
  390.     else
  391.     {
  392.         // Clear the global flag
  393.         g_bAudioOnly = FALSE;
  394.     }
  395.  
  396.     hr = pVW->get_Visible(&lVisible);
  397.     if (FAILED(hr))
  398.     {
  399.         // If this is an audio-only clip, get_Visible() won't work.
  400.         //
  401.         // Also, if this video is encoded with an unsupported codec,
  402.         // we won't see any video, although the audio will work if it is
  403.         // of a supported format.
  404.         //
  405.         if (hr == E_NOINTERFACE)
  406.         {
  407.             g_bAudioOnly = TRUE;
  408.         }
  409.         else
  410.         {
  411.             Msg(TEXT("Failed(%08lx) in pVW->get_Visible()!\r\n"), hr);
  412.         }
  413.     }
  414. }
  415.  
  416.  
  417. void PauseClip(void)
  418. {
  419.     if (!pMC)
  420.         return;
  421.  
  422.     // Toggle play/pause behavior
  423.     if((g_psCurrent == Paused) || (g_psCurrent == Stopped))
  424.     {
  425.         if (SUCCEEDED(pMC->Run()))
  426.             g_psCurrent = Running;
  427.     }
  428.     else
  429.     {
  430.         if (SUCCEEDED(pMC->Pause()))
  431.             g_psCurrent = Paused;
  432.     }
  433.  
  434.     UpdateMainTitle();
  435. }
  436.  
  437.  
  438. void StopClip(void)
  439. {
  440.     HRESULT hr;
  441.     
  442.     if ((!pMC) || (!pMS))
  443.         return;
  444.  
  445.     // Stop and reset postion to beginning
  446.     if((g_psCurrent == Paused) || (g_psCurrent == Running))
  447.     {
  448.         LONGLONG pos = 0;
  449.         hr = pMC->Stop();
  450.         g_psCurrent = Stopped;
  451.  
  452.         // Seek to the beginning
  453.         hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  454.             NULL, AM_SEEKING_NoPositioning);
  455.  
  456.         // Display the first frame to indicate the reset condition
  457.         hr = pMC->Pause();
  458.     }
  459.  
  460.     UpdateMainTitle();
  461. }
  462.  
  463.  
  464. void OpenClip()
  465. {
  466.     HRESULT hr;
  467.  
  468.     // If no filename specified by command line, show file open dialog
  469.     if(g_szFileName[0] == L'\0')
  470.     {
  471.         TCHAR szFilename[MAX_PATH];
  472.  
  473.         UpdateMainTitle();
  474.  
  475.         // If no filename was specified on the command line, then our video
  476.         // window has not been created or made visible.  Make our main window
  477.         // visible and bring to the front to allow file selection.
  478.         InitPlayerWindow();
  479.         ShowWindow(ghApp, SW_SHOWNORMAL);
  480.         SetForegroundWindow(ghApp);
  481.  
  482.         if (! GetClipFileName(szFilename))
  483.         {
  484.             DWORD dwDlgErr = CommDlgExtendedError();
  485.  
  486.             // Don't show output if user cancelled the selection (no dlg error)
  487.             if (dwDlgErr)
  488.             {
  489.                 Msg(TEXT("GetClipFileName Failed! Error=0x%x\r\n"), GetLastError());
  490.             }
  491.             return;
  492.         }
  493.  
  494.         // This sample does not support playback of ASX playlists.
  495.         // Since this could be confusing to a user, display a warning
  496.         // message if an ASX file was opened.
  497.         if (_tcsstr((_tcslwr(szFilename)), TEXT(".asx")))
  498.         {
  499.             Msg(TEXT("ASX Playlists are not supported by this application.\n\n")
  500.                 TEXT("Please select a valid media file.\0"));
  501.             return;
  502.         }
  503.  
  504.         lstrcpy(g_szFileName, szFilename);
  505.     }
  506.  
  507.     // Reset status variables
  508.     g_psCurrent = Stopped;
  509.     g_lVolume = VOLUME_FULL;
  510.     
  511.     // Start playing the media file
  512.     hr = PlayMovieInWindow(g_szFileName);
  513.  
  514.     // If we couldn't play the clip, clean up
  515.     if (FAILED(hr))
  516.         CloseClip();
  517. }
  518.  
  519.  
  520. BOOL GetClipFileName(LPTSTR szName)
  521. {
  522.     static OPENFILENAME ofn={0};
  523.     static BOOL bSetInitialDir = FALSE;
  524.  
  525.     // Reset filename
  526.     *szName = 0;
  527.  
  528.     // Fill in standard structure fields
  529.     ofn.lStructSize       = sizeof(OPENFILENAME);
  530.     ofn.hwndOwner         = ghApp;
  531.     ofn.lpstrFilter       = NULL;
  532.     ofn.lpstrFilter       = FILE_FILTER_TEXT;
  533.     ofn.lpstrCustomFilter = NULL;
  534.     ofn.nFilterIndex      = 1;
  535.     ofn.lpstrFile         = szName;
  536.     ofn.nMaxFile          = MAX_PATH;
  537.     ofn.lpstrTitle        = TEXT("Open Media File...\0");
  538.     ofn.lpstrFileTitle    = NULL;
  539.     ofn.lpstrDefExt       = TEXT("*\0");
  540.     ofn.Flags             = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST;
  541.     
  542.     // Remember the path of the first selected file
  543.     if (bSetInitialDir == FALSE)
  544.     {
  545.         ofn.lpstrInitialDir = DEFAULT_MEDIA_PATH;
  546.         bSetInitialDir = TRUE;
  547.     }
  548.     else 
  549.         ofn.lpstrInitialDir = NULL;
  550.  
  551.     // Create the standard file open dialog and return its result
  552.     return GetOpenFileName((LPOPENFILENAME)&ofn);
  553. }
  554.  
  555.  
  556. void CloseClip()
  557. {
  558.     HRESULT hr;
  559.  
  560.     // Stop media playback
  561.     if(pMC)
  562.         hr = pMC->Stop();
  563.  
  564.     // Clear global flags
  565.     g_psCurrent = Stopped;
  566.     g_bAudioOnly = TRUE;
  567.     g_bFullscreen = FALSE;
  568.  
  569.     // Free DirectShow interfaces
  570.     CloseInterfaces();
  571.  
  572.     // Clear file name to allow selection of new file with open dialog
  573.     g_szFileName[0] = L'\0';
  574.  
  575.     // No current media state
  576.     g_psCurrent = Init;
  577.  
  578.     // Reset the player window
  579.     RECT rect;
  580.     GetClientRect(ghApp, &rect);
  581.     InvalidateRect(ghApp, &rect, TRUE);
  582.  
  583.     UpdateMainTitle();
  584.     InitPlayerWindow();
  585. }
  586.  
  587.  
  588. void CloseInterfaces(void)
  589. {
  590.     HRESULT hr;
  591.     
  592.     // Relinquish ownership (IMPORTANT!) after hiding video window
  593.     if(pVW)
  594.     {
  595.         hr = pVW->put_Visible(OAFALSE);
  596.         hr = pVW->put_Owner(NULL);
  597.     }
  598.  
  599.     // Disable event callbacks
  600.     if (pME)
  601.         hr = pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
  602.  
  603. #ifdef REGISTER_FILTERGRAPH
  604.     if (g_dwGraphRegister)
  605.     {
  606.         RemoveGraphFromRot(g_dwGraphRegister);
  607.         g_dwGraphRegister = 0;
  608.     }
  609. #endif
  610.  
  611.     // Release and zero DirectShow interfaces
  612.     SAFE_RELEASE(pME);
  613.     SAFE_RELEASE(pMS);
  614.     SAFE_RELEASE(pMC);
  615.     SAFE_RELEASE(pBA);
  616.     SAFE_RELEASE(pBV);
  617.     SAFE_RELEASE(pVW);
  618.     SAFE_RELEASE(pFS);
  619.     SAFE_RELEASE(pGB);
  620. }
  621.  
  622.  
  623. #ifdef REGISTER_FILTERGRAPH
  624.  
  625. HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) 
  626. {
  627.     IMoniker * pMoniker;
  628.     IRunningObjectTable *pROT;
  629.     if (FAILED(GetRunningObjectTable(0, &pROT))) 
  630.     {
  631.         return E_FAIL;
  632.     }
  633.  
  634.     WCHAR wsz[128];
  635.     wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, 
  636.               GetCurrentProcessId());
  637.  
  638.     HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
  639.     if (SUCCEEDED(hr)) 
  640.     {
  641.         hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister);
  642.         pMoniker->Release();
  643.     }
  644.  
  645.     pROT->Release();
  646.     return hr;
  647. }
  648.  
  649. void RemoveGraphFromRot(DWORD pdwRegister)
  650. {
  651.     IRunningObjectTable *pROT;
  652.  
  653.     if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) 
  654.     {
  655.         pROT->Revoke(pdwRegister);
  656.         pROT->Release();
  657.     }
  658. }
  659.  
  660. #endif
  661.  
  662.  
  663. void Msg(TCHAR *szFormat, ...)
  664. {
  665.     TCHAR szBuffer[512];  // Large buffer for very long filenames (like HTTP)
  666.  
  667.     // Format the input string
  668.     va_list pArgs;
  669.     va_start(pArgs, szFormat);
  670.     _vstprintf(szBuffer, szFormat, pArgs);
  671.     va_end(pArgs);
  672.  
  673.     // Display a message box with the formatted string
  674.     MessageBox(NULL, szBuffer, TEXT("PlayWndASF Sample"), MB_OK);
  675. }
  676.  
  677.  
  678. HRESULT ToggleMute(void)
  679. {
  680.     HRESULT hr=S_OK;
  681.  
  682.     if ((!pGB) || (!pBA))
  683.         return S_OK;
  684.  
  685.     // Read current volume
  686.     hr = pBA->get_Volume(&g_lVolume);
  687.     if (hr == E_NOTIMPL)
  688.     {
  689.         // Fail quietly if this is a video-only media file
  690.         return S_OK;
  691.     }
  692.     else if (FAILED(hr))
  693.     {
  694.         Msg(TEXT("Failed to read audio volume!  hr=0x%x\r\n"), hr);
  695.         return hr;
  696.     }
  697.  
  698.     // Switch volume levels
  699.     if (g_lVolume == VOLUME_FULL)
  700.         g_lVolume = VOLUME_SILENCE;
  701.     else
  702.         g_lVolume = VOLUME_FULL;
  703.  
  704.     // Set new volume
  705.     JIF(pBA->put_Volume(g_lVolume));
  706.  
  707.     UpdateMainTitle();
  708.     return hr;
  709. }
  710.  
  711.  
  712. void UpdateMainTitle(void)
  713. {
  714.     TCHAR szTitle[MAX_PATH], szFile[MAX_PATH];
  715.  
  716.     // If no file is loaded, just show the application title
  717.     if (g_szFileName[0] == L'\0')
  718.     {
  719.         wsprintf(szTitle, TEXT("%s"), APPLICATIONNAME);
  720.     }
  721.  
  722.     // Otherwise, show useful information
  723.     else
  724.     {
  725.         // Get file name without full path
  726.         GetFilename(g_szFileName, szFile);
  727.  
  728.         // Update the window title to show filename and play state
  729.         wsprintf(szTitle, TEXT("%s [%s] %s%s"),
  730.                 szFile,
  731.                 g_bAudioOnly ? TEXT("Audio") : TEXT("Video"),
  732.                 (g_lVolume == VOLUME_SILENCE) ? TEXT("(Muted)") : TEXT(""),
  733.                 (g_psCurrent == Paused) ? TEXT("(Paused)") : TEXT(""));
  734.     }
  735.  
  736.     SetWindowText(ghApp, szTitle);
  737. }
  738.  
  739.  
  740. void GetFilename(TCHAR *pszFull, TCHAR *pszFile)
  741. {
  742.     int nLength;
  743.     TCHAR szPath[MAX_PATH]={0};
  744.     BOOL bSetFilename=FALSE;
  745.  
  746.     // Strip path and return just the file's name
  747.     _tcscpy(szPath, pszFull);
  748.     nLength = _tcslen(szPath);
  749.  
  750.     for (int i=nLength-1; i>=0; i--)
  751.     {
  752.         if ((szPath[i] == '\\') || (szPath[i] == '/'))
  753.         {
  754.             szPath[i] = '\0';
  755.             lstrcpy(pszFile, &szPath[i+1]);
  756.             bSetFilename = TRUE;
  757.             break;
  758.         }
  759.     }
  760.  
  761.     // If there was no path given (just a file name), then
  762.     // just copy the full path to the target path.
  763.     if (!bSetFilename)
  764.         _tcscpy(pszFile, pszFull);
  765. }
  766.  
  767.  
  768. HRESULT ToggleFullScreen(void)
  769. {
  770.     HRESULT hr=S_OK;
  771.     LONG lMode;
  772.     static HWND hDrain=0;
  773.  
  774.     // Don't bother with full-screen for audio-only files
  775.     if ((g_bAudioOnly) || (!pVW))
  776.         return S_OK;
  777.  
  778.     // Read current state
  779.     JIF(pVW->get_FullScreenMode(&lMode));
  780.  
  781.     if (lMode == OAFALSE)
  782.     {
  783.         // Save current message drain
  784.         LIF(pVW->get_MessageDrain((OAHWND *) &hDrain));
  785.  
  786.         // Set message drain to application main window
  787.         LIF(pVW->put_MessageDrain((OAHWND) ghApp));
  788.  
  789.         // Switch to full-screen mode
  790.         lMode = OATRUE;
  791.         JIF(pVW->put_FullScreenMode(lMode));
  792.         g_bFullscreen = TRUE;
  793.     }
  794.     else
  795.     {
  796.         // Switch back to windowed mode
  797.         lMode = OAFALSE;
  798.         JIF(pVW->put_FullScreenMode(lMode));
  799.  
  800.         // Undo change of message drain
  801.         LIF(pVW->put_MessageDrain((OAHWND) hDrain));
  802.  
  803.         // Reset video window
  804.         LIF(pVW->SetWindowForeground(-1));
  805.  
  806.         // Reclaim keyboard focus for player application
  807.         UpdateWindow(ghApp);
  808.         SetForegroundWindow(ghApp);
  809.         SetFocus(ghApp);
  810.         g_bFullscreen = FALSE;
  811.     }
  812.  
  813.     return hr;
  814. }
  815.  
  816.  
  817. //
  818. // Some video renderers support stepping media frame by frame with the 
  819. // IVideoFrameStep interface.  See the interface documentation for more 
  820. // details on frame stepping.
  821. //
  822. BOOL GetFrameStepInterface(void)
  823. {
  824.     HRESULT hr;
  825.     IVideoFrameStep *pFSTest = NULL;
  826.  
  827.     // Get the frame step interface, if supported
  828.     hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFSTest);
  829.     if (FAILED(hr))
  830.         return FALSE;
  831.  
  832.     // Check if this decoder can step
  833.     hr = pFSTest->CanStep(0L, NULL); 
  834.  
  835.     if (hr == S_OK)
  836.     {
  837.         pFS = pFSTest;  // Save interface to global variable for later use
  838.         return TRUE;
  839.     }
  840.     else
  841.     {
  842.         pFSTest->Release();
  843.         return FALSE;
  844.     }
  845. }
  846.  
  847.  
  848. HRESULT StepOneFrame(void)
  849. {
  850.     HRESULT hr=S_OK;
  851.  
  852.     // If the Frame Stepping interface exists, use it to step one frame
  853.     if (pFS)
  854.     {
  855.         // The graph must be paused for frame stepping to work
  856.         if (g_psCurrent != State_Paused)
  857.             PauseClip();
  858.  
  859.         // Step the requested number of frames, if supported
  860.         hr = pFS->Step(1, NULL); 
  861.     }
  862.  
  863.     return hr;
  864. }
  865.  
  866. HRESULT StepFrames(int nFramesToStep)
  867. {
  868.     HRESULT hr=S_OK;
  869.  
  870.     // If the Frame Stepping interface exists, use it to step frames
  871.     if (pFS)
  872.     {
  873.         // The renderer may not support frame stepping for more than one
  874.         // frame at a time, so check for support.  S_OK indicates that the
  875.         // renderer can step nFramesToStep successfully.
  876.         if ((hr = pFS->CanStep(nFramesToStep, NULL)) == S_OK)
  877.         {
  878.             // The graph must be paused for frame stepping to work
  879.             if (g_psCurrent != State_Paused)
  880.                 PauseClip();
  881.  
  882.             // Step the requested number of frames, if supported
  883.             hr = pFS->Step(nFramesToStep, NULL); 
  884.         }
  885.     }
  886.  
  887.     return hr;
  888. }
  889.  
  890.  
  891. HRESULT HandleGraphEvent(void)
  892. {
  893.     LONG evCode, evParam1, evParam2;
  894.     HRESULT hr=S_OK;
  895.  
  896.     // Make sure that we don't access the media event interface
  897.     // after it has already been released.
  898.     if (!pME)
  899.         return S_OK;
  900.  
  901.     // Process all queued events
  902.     while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
  903.                     (LONG_PTR *) &evParam2, 0)))
  904.     {
  905.         // Free memory associated with callback, since we're not using it
  906.         hr = pME->FreeEventParams(evCode, evParam1, evParam2);
  907.  
  908.         // If this is the end of the clip, reset to beginning
  909.         if(EC_COMPLETE == evCode)
  910.         {
  911.             LONGLONG pos=0;
  912.  
  913.             // Reset to first frame of movie
  914.             hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  915.                                    NULL, AM_SEEKING_NoPositioning);
  916.             if (FAILED(hr))
  917.             {
  918.                 // Some custom filters (like the Windows CE MIDI filter) 
  919.                 // may not implement seeking interfaces (IMediaSeeking)
  920.                 // to allow seeking to the start.  In that case, just stop 
  921.                 // and restart for the same effect.  This should not be
  922.                 // necessary in most cases.
  923.                 if (FAILED(hr = pMC->Stop()))
  924.                 {
  925.                     Msg(TEXT("Failed(0x%08lx) to stop media clip!\r\n"), hr);
  926.                     break;
  927.                 }
  928.  
  929.                 if (FAILED(hr = pMC->Run()))
  930.                 {
  931.                     Msg(TEXT("Failed(0x%08lx) to reset media clip!\r\n"), hr);
  932.                     break;
  933.                 }
  934.             }
  935.         }
  936.     }
  937.  
  938.     return hr;
  939. }
  940.  
  941.  
  942. void CheckSizeMenu(WPARAM wParam)
  943. {
  944.     WPARAM nItems[4] = {ID_FILE_SIZE_HALF,    ID_FILE_SIZE_DOUBLE, 
  945.                         ID_FILE_SIZE_NORMAL,  ID_FILE_SIZE_THREEQUARTER};
  946.  
  947.     // Set/clear checkboxes that indicate the size of the video clip
  948.     for (int i=0; i<4; i++)
  949.     {
  950.         // Check the selected item
  951.         CheckMenuItem(ghMenu, (UINT) nItems[i],
  952.                      (UINT) (wParam == nItems[i]) ? MF_CHECKED : MF_UNCHECKED);
  953.     }
  954. }
  955.  
  956.  
  957. void EnablePlaybackMenu(BOOL bEnable)
  958. {
  959.     WPARAM nItems[9] = {ID_FILE_PAUSE,        ID_FILE_STOP,
  960.                         ID_FILE_MUTE,         ID_SINGLE_STEP,
  961.                         ID_FILE_SIZE_HALF,    ID_FILE_SIZE_DOUBLE,
  962.                         ID_FILE_SIZE_NORMAL,  ID_FILE_SIZE_THREEQUARTER,
  963.                         ID_FILE_FULLSCREEN};
  964.  
  965.     // Set/clear checkboxes that indicate the size of the video clip
  966.     for (int i=0; i<9; i++)
  967.     {
  968.         // Check the selected item
  969.         EnableMenuItem(ghMenu, (UINT) nItems[i],
  970.                      (UINT) (bEnable) ? MF_ENABLED : MF_GRAYED);
  971.     }
  972. }
  973.  
  974. LRESULT CALLBACK AboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  975. {
  976.     switch (message)
  977.     {
  978.         case WM_INITDIALOG:
  979.             return TRUE;
  980.  
  981.         case WM_COMMAND:
  982.             if (wParam == IDOK)
  983.             {   
  984.                 EndDialog(hWnd, TRUE);
  985.                 return TRUE;
  986.             }
  987.             break;
  988.     }
  989.     return FALSE;
  990. }
  991.  
  992.  
  993. LRESULT CALLBACK WndMainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  994. {
  995.     switch(message)
  996.     {
  997.         // Resize the video when the window changes
  998.         case WM_MOVE:
  999.         case WM_SIZE:
  1000.             if ((hWnd == ghApp) && (!g_bAudioOnly))
  1001.                 MoveVideoWindow();
  1002.             break;
  1003.  
  1004.         // Enforce a minimum size
  1005.         case WM_GETMINMAXINFO:
  1006.             {
  1007.                 LPMINMAXINFO lpmm = (LPMINMAXINFO) lParam;
  1008.                 lpmm->ptMinTrackSize.x = MINIMUM_VIDEO_WIDTH;
  1009.                 lpmm->ptMinTrackSize.y = MINIMUM_VIDEO_HEIGHT;
  1010.             }
  1011.             break;
  1012.  
  1013.         case WM_KEYDOWN:
  1014.  
  1015.             switch(toupper((int) wParam))
  1016.             {
  1017.                 // Frame stepping
  1018.                 case VK_SPACE:
  1019.                 case '1':
  1020.                     StepOneFrame();
  1021.                     break;
  1022.  
  1023.                 // Frame stepping (multiple frames)
  1024.                 case '2':
  1025.                 case '3':
  1026.                 case '4':
  1027.                 case '5':
  1028.                 case '6':
  1029.                 case '7':
  1030.                 case '8':
  1031.                 case '9':
  1032.                     StepFrames((int) wParam - '0');
  1033.                     break;
  1034.  
  1035.                 case 'P':
  1036.                     PauseClip();
  1037.                     break;
  1038.  
  1039.                 case 'S':
  1040.                     StopClip();
  1041.                     break;
  1042.  
  1043.                 case 'M':
  1044.                     ToggleMute();
  1045.                     break;
  1046.  
  1047.                 case 'F':
  1048.                 case VK_RETURN:
  1049.                     ToggleFullScreen();
  1050.                     break;
  1051.  
  1052.                case 'H':
  1053.                     InitVideoWindow(1,2);
  1054.                     CheckSizeMenu(wParam);
  1055.                     break;
  1056.                 case 'N':
  1057.                     InitVideoWindow(1,1);
  1058.                     CheckSizeMenu(wParam);
  1059.                     break;
  1060.                 case 'D':
  1061.                     InitVideoWindow(2,1);
  1062.                     CheckSizeMenu(wParam);
  1063.                     break;
  1064.                 case 'T':
  1065.                     InitVideoWindow(3,4);
  1066.                     CheckSizeMenu(wParam);
  1067.                     break;
  1068.  
  1069.                 case VK_ESCAPE:
  1070.                     if (g_bFullscreen)
  1071.                         ToggleFullScreen();
  1072.                     else
  1073.                         CloseClip();
  1074.                     break;
  1075.  
  1076.                 case VK_F12:
  1077.                 case 'Q':
  1078.                 case 'X':
  1079.                     CloseClip();
  1080.                     break;
  1081.             }
  1082.             break;
  1083.  
  1084.         case WM_COMMAND:
  1085.  
  1086.             switch(wParam)
  1087.             { // Menus
  1088.  
  1089.                 case ID_FILE_OPENCLIP:
  1090.                     // If we have ANY file open, close it and shut down DShow
  1091.                     if (g_psCurrent != Init)
  1092.                         CloseClip();
  1093.  
  1094.                     // Open the new clip
  1095.                     OpenClip();
  1096.                     break;
  1097.  
  1098.                 case ID_FILE_EXIT:
  1099.                     CloseClip();
  1100.                     PostQuitMessage(0);
  1101.                     break;
  1102.  
  1103.                 case ID_FILE_PAUSE:
  1104.                     PauseClip();
  1105.                     break;
  1106.  
  1107.                 case ID_FILE_STOP:
  1108.                     StopClip();
  1109.                     break;
  1110.  
  1111.                 case ID_FILE_CLOSE:
  1112.                     CloseClip();
  1113.                     break;
  1114.  
  1115.                 case ID_FILE_MUTE:
  1116.                     ToggleMute();
  1117.                     break;
  1118.  
  1119.                 case ID_FILE_FULLSCREEN:
  1120.                     ToggleFullScreen();
  1121.                     break;
  1122.  
  1123.                 case ID_HELP_ABOUT:
  1124.                     DialogBox(ghInst, MAKEINTRESOURCE(IDD_ABOUTBOX), 
  1125.                               ghApp,  (DLGPROC) AboutDlgProc);
  1126.                     break;
  1127.  
  1128.                 case ID_FILE_SIZE_HALF:
  1129.                     InitVideoWindow(1,2);
  1130.                     CheckSizeMenu(wParam);
  1131.                     break;
  1132.                 case ID_FILE_SIZE_NORMAL:
  1133.                     InitVideoWindow(1,1);
  1134.                     CheckSizeMenu(wParam);
  1135.                     break;
  1136.                 case ID_FILE_SIZE_DOUBLE:
  1137.                     InitVideoWindow(2,1);
  1138.                     CheckSizeMenu(wParam);
  1139.                     break;
  1140.                 case ID_FILE_SIZE_THREEQUARTER:
  1141.                     InitVideoWindow(3,4);
  1142.                     CheckSizeMenu(wParam);
  1143.                     break;
  1144.  
  1145.                 case ID_SINGLE_STEP:
  1146.                     StepOneFrame();
  1147.                     break;
  1148.  
  1149.                 case ID_RENDERING_USENEWASFREADER:
  1150.                     g_bUseNewASFReader=TRUE;
  1151.                     CheckMenuItem(ghMenu, ID_RENDERING_USENEWASFREADER, MF_CHECKED);
  1152.                     CheckMenuItem(ghMenu, ID_RENDERING_USELEGACYASFREADER, MF_UNCHECKED);
  1153.                     break;
  1154.  
  1155.                 case ID_RENDERING_USELEGACYASFREADER:
  1156.                     g_bUseNewASFReader=FALSE;
  1157.                     CheckMenuItem(ghMenu, ID_RENDERING_USENEWASFREADER, MF_UNCHECKED);
  1158.                     CheckMenuItem(ghMenu, ID_RENDERING_USELEGACYASFREADER, MF_CHECKED);
  1159.                     break;
  1160.             } // Menus
  1161.             break;
  1162.  
  1163.  
  1164.         case WM_GRAPHNOTIFY:
  1165.             HandleGraphEvent();
  1166.             break;
  1167.  
  1168.         case WM_CLOSE:
  1169.             SendMessage(ghApp, WM_COMMAND, ID_FILE_EXIT, 0);
  1170.             break;
  1171.  
  1172.         case WM_DESTROY:
  1173.             PostQuitMessage(0);
  1174.             break;
  1175.  
  1176.         default:
  1177.             return DefWindowProc(hWnd, message, wParam, lParam);
  1178.  
  1179.     } // Window msgs handling
  1180.  
  1181.     // Pass this message to the video window for notification of system changes
  1182.     if (pVW)
  1183.         pVW->NotifyOwnerMessage((LONG_PTR) hWnd, message, wParam, lParam);
  1184.  
  1185.     return DefWindowProc(hWnd, message, wParam, lParam);
  1186. }
  1187.  
  1188.  
  1189. int PASCAL WinMain(HINSTANCE hInstC, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow)
  1190. {
  1191.     MSG msg={0};
  1192.     WNDCLASS wc;
  1193.  
  1194.     // Initialize COM
  1195.     if(FAILED(CoInitialize(NULL)))
  1196.     {
  1197.         Msg(TEXT("CoInitialize Failed!\r\n"));
  1198.         exit(1);
  1199.     }
  1200.  
  1201.     // Was a filename specified on the command line?
  1202.     if(lpCmdLine[0] != '\0')
  1203. #ifdef UNICODE
  1204.         MultiByteToWideChar(CP_ACP, 0, lpCmdLine, -1, g_szFileName, MAX_PATH);       
  1205. #else
  1206.         lstrcpy(g_szFileName, lpCmdLine);
  1207. #endif
  1208.  
  1209.     // Set initial media state
  1210.     g_psCurrent = Init;
  1211.  
  1212.     // Register the window class
  1213.     ZeroMemory(&wc, sizeof wc);
  1214.     wc.lpfnWndProc = WndMainProc;
  1215.     ghInst = wc.hInstance = hInstC;
  1216.     wc.lpszClassName = CLASSNAME;
  1217.     wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU);
  1218.     wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  1219.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  1220.     wc.hIcon         = LoadIcon(hInstC, MAKEINTRESOURCE(IDI_PLAYWND));
  1221.     if(!RegisterClass(&wc))
  1222.     {
  1223.         Msg(TEXT("RegisterClass Failed! Error=0x%x\r\n"), GetLastError());
  1224.         CoUninitialize();
  1225.         exit(1);
  1226.     }
  1227.  
  1228.     // Create the main window.  The WS_CLIPCHILDREN style is required.
  1229.     ghApp = CreateWindow(CLASSNAME, APPLICATIONNAME,
  1230.                     WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN,
  1231.                     CW_USEDEFAULT, CW_USEDEFAULT,
  1232.                     CW_USEDEFAULT, CW_USEDEFAULT,
  1233.                     0, 0, ghInst, 0);
  1234.  
  1235.     if(ghApp)
  1236.     {
  1237.         // Save menu handle for later use
  1238.         ghMenu = GetMenu(ghApp);
  1239.  
  1240.         // Open the specified media file or prompt for a title
  1241.         PostMessage(ghApp, WM_COMMAND, ID_FILE_OPENCLIP, 0);
  1242.  
  1243.         // Main message loop
  1244.         while(GetMessage(&msg,NULL,0,0))
  1245.         {
  1246.             TranslateMessage(&msg);
  1247.             DispatchMessage(&msg);
  1248.         }
  1249.     }
  1250.     else
  1251.     {
  1252.         Msg(TEXT("Failed to create the main window! Error=0x%x\r\n"), GetLastError());
  1253.     }
  1254.  
  1255.     // Finished with COM
  1256.     CoUninitialize();
  1257.  
  1258.     return (int) msg.wParam;
  1259. }
  1260.  
  1261.  
  1262.